#include "task.h"
#include <string.h>
#include <sys/socket.h>
#define MAX_FILE_SIZE 1024
int recvn(int sockfd, void *buf, int length) {
  int total = 0;
  char *p = (char *)buf;
  while (total < length) {
    ssize_t sret = recv(sockfd, p + total, length - total, 0);
    total += sret;
    // if(sret < 0){
    //     printf("error in  recvn\n");
    //     return -1;
    // }
  }
  return total;
}
void getMD5(const char *filename, char *result) {
  MD5_CTX md5;
  MD5_Init(&md5);
  int fd = open(filename, O_RDWR);
  ERROR_CHECK(fd, -1, "open");
  char buf[4096];
  while (1) {
    bzero(buf, sizeof(buf));
    ssize_t sret = read(fd, buf, sizeof(buf));
    if (sret == 0) {
      break;
    }
    MD5_Update(&md5, buf, sret);
  }
  unsigned char md[16];
  MD5_Final(md, &md5);
  char md5_str[33] = {0};
  for (int i = 0; i < 16; i++) {
    char temp[3] = {0};
    sprintf(temp, "%02x", md[i]);
    strcat(md5_str, temp);
  }
  close(fd);
  strncpy(result, md5_str, 33);
}
int up_load(int netfd, char *filename) {
  train_t train;
  train.type = PUTS;
  int fd = open(filename, O_RDONLY);
  train.length = strlen(filename);
  memcpy(train.data, filename, train.length);
  if (fd < 0) {
    train.type = FAILURE;
    send(netfd, &train.type, sizeof(int), MSG_NOSIGNAL);
    printf("file not exit\n");
    return -1;
  }
  send(netfd, &train.type, sizeof(int), MSG_NOSIGNAL);
  send(netfd, &train.length, sizeof(train.length), MSG_NOSIGNAL);
  send(netfd, train.data, train.length, MSG_NOSIGNAL);
  struct stat statbuf;
  fstat(fd, &statbuf);
  off_t filesize = statbuf.st_size;
  train.length = sizeof(filesize);
  memcpy(train.data, &filesize, train.length);
  send(netfd, &train.type, sizeof(int), MSG_NOSIGNAL);
  send(netfd, &train.length, sizeof(int), MSG_NOSIGNAL);
  send(netfd, train.data, train.length, MSG_NOSIGNAL);
  while (1) {
    bzero(train.data, sizeof(train.data));
    ssize_t sret = read(fd, train.data, sizeof(train.length));
    train.length = sret;
    send(netfd, &train.length, sizeof(train.length), MSG_NOSIGNAL);
    send(netfd, train.data, train.length, MSG_NOSIGNAL);
    if (sret == 0) {
      break;
    }
  }
  close(fd);
  return 0;
}
// int down_load(int netfd, char *filename) {
//   train_t train;

//   // 接收文件大小
//   off_t file_size = 0;
//  // recvn(netfd, &train.type, sizeof(train.type));
//   recvn(netfd, &train.length, sizeof(train.length));
//   recvn(netfd, train.data, train.length);
//   memcpy(&file_size, train.data, sizeof(off_t));

//   // 检查本地是否已存在（断点续传）
//   int fd = open(filename, O_WRONLY | O_CREAT, 0666);
//   off_t existing_size = lseek(fd, 0, SEEK_END);
//   send(netfd, &existing_size, sizeof(off_t), 0); // 发送已下载字节数

//   off_t cursize = existing_size;
//   off_t lastsize = existing_size;
//   off_t slice = file_size / 10000;

//   // 判断大文件
//   if (file_size > 100 * 1024 * 1024) {
//     while (cursize < file_size) {
//       int datalen = 0;
//       int ret = recvn(netfd, &datalen, sizeof(int));
//       if (ret <= 0 || datalen == 0)
//         break;
//       ret = recvn(netfd, train.data, datalen);
//       if (ret <= 0)
//         break;
//       write(fd, train.data, datalen);
//       cursize += datalen;
//       if (cursize - lastsize > slice) {
//         printf("%5.2lf%%\r", 100.0 * cursize / file_size);
//         fflush(stdout);
//         lastsize = cursize;
//       }
//     }
//   } else {
//     // 普通接收数据
//     while (1) {
//       int datalen = 0;
//       recvn(netfd, &datalen, sizeof(int));
//       if (datalen == 0)
//         break;
//       recvn(netfd, train.data, datalen);
//       write(fd, train.data, datalen);
//       cursize += datalen;
//       if (cursize - lastsize > slice) {
//         printf("%5.2lf%%\r", 100.0 * cursize / file_size);
//         fflush(stdout);
//         lastsize = cursize;
//       }
//     }
//   }
//   printf("100.00%%\n");
//   close(fd);
//   return 0;
// }
int down_load(int netfd, char *filename) {
  train_t train;

//接收文件名
recvn(netfd,&train.length,sizeof(train.length));
recvn(netfd,train.data,train.length);
  // 接收文件大小
  off_t file_size = 0;
 // recvn(netfd, &train.type, sizeof(train.type));
  recvn(netfd, &train.length, sizeof(train.length));
  recvn(netfd, train.data, train.length);
  memcpy(&file_size, train.data, sizeof(off_t));

  // 检查本地是否已存在（断点续传）
  int fd = open(filename, O_WRONLY | O_CREAT, 0666);
  off_t existing_size = lseek(fd, 0, SEEK_END);
  send(netfd, &existing_size, sizeof(off_t), 0); // 发送已下载字节数

  off_t cursize = existing_size;
  off_t lastsize = existing_size;
  off_t slice = file_size / 10000;

  // 判断大文件
  if (file_size > 100 * 1024 * 1024) {
    while (1) {
        //int endFlag = 0;
        ssize_t to_recv = file_size - cursize;
        if (to_recv > sizeof(train.data)) to_recv = sizeof(train.data);
        ssize_t ret = recvn(netfd, train.data, to_recv);
        //printf("%d\n",ret);
        if (ret <= 0) {
          break;
        }
        write(fd, train.data, ret);
        cursize += ret;
        if (cursize - lastsize > slice) {
            printf("%5.2lf%%\r", 100.0 * cursize / file_size);
            fflush(stdout);
            lastsize = cursize;
        }
    
    }
  }
   else {
        // 小文件分块接收
        while (1) {
          bzero(train.data, sizeof(train.data));
            int datalen = 0;
            recvn(netfd, &datalen, sizeof(int));
            printf("datalen%d",datalen);
            if (datalen == 0){
              printf("1\n");
              break;
            }
            recvn(netfd, train.data, datalen);
            write(fd, train.data, datalen);
            cursize += datalen;
            if (cursize - lastsize > slice) {
                printf("%5.2lf%%\r", 100.0 * cursize / file_size);
                fflush(stdout);
                lastsize = cursize;
            }
        }
    }
  printf("100.00%%\n");
  close(fd);
  return 0;
}
//0723night
int send_big_file(int netfd, int fd, off_t existsize, off_t totalSize) {
    // 映射文件到内存
    char *pmap = mmap(NULL, totalSize, PROT_READ, MAP_SHARED, fd, 0);
    if (pmap == MAP_FAILED) {
        perror("mmap");
        return -1;
    }
    
    off_t offset = existsize;
    off_t remain = totalSize - existsize;
    const int MAX_BLOCK_SIZE = 1048576; // 1MB
    
    while (remain > 0) {
        int blockSize = (remain > MAX_BLOCK_SIZE) ? MAX_BLOCK_SIZE : remain;
        
        // 发送块大小
        send(netfd, &blockSize, sizeof(int), MSG_NOSIGNAL);
        
        // 发送数据块
        send(netfd, pmap + offset, blockSize, MSG_NOSIGNAL);
        
        offset += blockSize;
        remain -= blockSize;
    }
    
    munmap(pmap, totalSize);
    return 0;
}
//0723night
int handle_puts(int netfd,  char *filename) {
  printf("客户端puts\n");
  train_t train;
  bzero(train.data, sizeof(train.data));
  strcat(train.data, filename);
  train.length=strlen(filename);
  send(netfd, &train.length, sizeof(train.length), MSG_NOSIGNAL);
  send(netfd, train.data, train.length, MSG_NOSIGNAL);
  char md5[1024] = {0};
  getMD5(filename, md5);
  bzero(&train,sizeof(train));
  train.length=strlen(md5);
  strcpy(train.data,md5);
  send(netfd, &train.length, sizeof(train.length), MSG_NOSIGNAL);
  send(netfd, train.data, train.length, MSG_NOSIGNAL);
  // md5发送完毕
  // 根据字节数查看是否秒传
  //char tmp[1024] = {"秒传"};
  //char bufs[1024] = {0};
  int ismiaochuan=0;
  int types_count = recvn(netfd, &ismiaochuan, sizeof(int));
  if (types_count <= 0) {
    printf("秒传消息接受失败");
    return -1;
  }
  if (ismiaochuan== 1) {
    printf("秒传");
    return 0;
  }
  off_t existsize = 0;
  recvn(netfd, &existsize, sizeof(off_t));
  // char local_path[1024] = {0};
  // strcat(local_path, client_dir);
  // strcat(local_path, filename);
  int fd = open(filename, O_RDWR | O_CREAT , 0666);
  if (fd == -1) {
    printf("打开文件失败\n");
    return -1;
  }
  struct stat fileStat;
  fstat(fd, &fileStat);
off_t totalSize = fileStat.st_size;
send(netfd, &totalSize, sizeof(off_t), MSG_NOSIGNAL);
//0723night
// 6.大文件处理
    if (totalSize > MAX_FILE_SIZE) {
        send_big_file(netfd, fd, existsize, totalSize);
    } else {
  lseek(fd, existsize, SEEK_SET);
  while (1) {
    bzero(train.data, sizeof(train.data));
    ssize_t sret = read(fd, train.data, sizeof(train.data));
    train.length = sret;
    send(netfd, &train.length, sizeof(train.length), MSG_NOSIGNAL);
    send(netfd, train.data, train.length, MSG_NOSIGNAL);
    if (sret == 0) {
      break;
    }
  }
}
  return 0;
}
int send_command(int sockfd, Type type, const char *result) {
  train_t train;
  train.type = type;
  /* printf("%d\n",type); */
  send(sockfd, &train.type, sizeof(train.type), MSG_NOSIGNAL);
  if (*result) {
    train.length = strlen(result);
    send(sockfd, &train.length, sizeof(train.length), MSG_NOSIGNAL);
    memcpy(train.data, result, train.length);
    send(sockfd, train.data, train.length, MSG_NOSIGNAL);
  }
  return train.type;
}
int recv_response(int sockfd) {
  train_t train;
  /* recvn(sockfd, &train.type, sizeof(train.type)); */
  recvn(sockfd, &train.length, sizeof(train.length));
  if (train.length > 0) {
    recvn(sockfd, train.data, train.length);
    train.data[train.length] = '\0';
    printf("%s\n", train.data);
  }

  return 0;
}
